<?php
/**
 * Created by PhpStorm.
 * User: jaylen
 * Date: 2020-04-14
 * Time: 21:51
 */

namespace app\admin\model;


use app\common\exception\HaveUseException;
use app\common\exception\ParameterException;
use app\common\model\BaseModel;
use app\common\validate\IDCollection;
use app\common\validate\IDMustBeRequire;
use think\model\concern\SoftDelete;
use app\admin\validate\AuthRole as Validate;
use app\admin\model\AuthRoleMenu as AuthRoleMenuModel;
use app\admin\model\AuthRoleHalfMenu as AuthRoleHalfMenuModel;
use app\admin\model\AuthRoleRule as AuthRoleRuleModel;
use app\admin\model\Admin as AdminModel;

class AuthRole extends BaseModel
{
    protected $hidden = ['update_time','delete_time'];

    // 使用软删除
    use SoftDelete;
    protected $deleteTime = 'delete_time';

    public function menus()
    {
        // belongsToMany('关联模型','中间表','外键','关联键');
        // 关联模型（必须）：关联模型类名
        // 中间表：默认规则是当前模型名+_+关联模型名 （可以指定模型名）
        // 外键：中间表的当前模型外键，默认的外键名规则是关联模型名+_id
        // 关联键：中间表的当前模型关联键名，默认规则是当前模型名+_id
        return $this->belongsToMany('AuthMenu','\\app\\admin\\model\\AuthRoleMenu','menu_id','role_id');
    }

    public function half_menus()
    {
        // belongsToMany('关联模型','中间表','外键','关联键');
        // 关联模型（必须）：关联模型类名
        // 中间表：默认规则是当前模型名+_+关联模型名 （可以指定模型名）
        // 外键：中间表的当前模型外键，默认的外键名规则是关联模型名+_id
        // 关联键：中间表的当前模型关联键名，默认规则是当前模型名+_id
        return $this->belongsToMany('AuthMenu','\\app\\admin\\model\\AuthRoleHalfMenu','menu_id','role_id');
    }

    public function rules()
    {
        // belongsToMany('关联模型','中间表','外键','关联键');
        // 关联模型（必须）：关联模型类名
        // 中间表：默认规则是当前模型名+_+关联模型名 （可以指定模型名）
        // 外键：中间表的当前模型外键，默认的外键名规则是关联模型名+_id
        // 关联键：中间表的当前模型关联键名，默认规则是当前模型名+_id
        return $this->belongsToMany('AuthRule','\\app\\admin\\model\\AuthRoleRule','rule_id','role_id');
    }

    /**
     * 获取权限角色的列表信息
     * @param array $params
     * @return \think\Paginator
     */
    public static function getPaginationList(array $params)
    {
        static::validatePaginationData($params);

        $static = new static();

        foreach ($params as $name => $value) {
            $value = trim($value);
            switch ($name) {
                case 'title':
                    if (!empty($value)) {
                        $like_text = '%' . $value . '%';
                        $static = $static->whereLike('title', $like_text);
                    }
                    break;
                case 'name':
                    if (!empty($value)) {
                        $like_text = '%' . $value . '%';
                        $static = $static->whereLike('name', $like_text);
                    }
                    break;
            }
        }

        return $static->paginate([
            'page' => $params['page'],
            'list_rows' => $params['limit']
        ], false);
    }

    /**
     * 根据id获取角色信息
     * @param $id
     * @return mixed
     */
    public static function getByID($id)
    {
        $validate = new IDMustBeRequire();
        if (!$validate->check(['id' => $id])) {
            throw new ParameterException($validate->getError());
        }

        $static = static::find($id);

        $data = $static->getData();


        $menus = array_diff($static->allowMenuIDs(), $static->allowHalfMenuIDs());
        $menus = array_values($menus);

        $data['menus'] = $menus;
        $data['rules'] = $static->allowRuleIDs();

        return $data;
    }

    /**
     * 添加权限角色信息
     * @param array $data
     * @return bool
     */
    public static function addAuthRule(array $data)
    {
        $validate = new Validate();
        if (!$validate->scene('add')->check($data)) {
            throw new ParameterException([
                'msg' => $validate->getError()
            ]);
        }

        // 使用事务保证数据的一致性
        $static = new static();
        $static->startTrans();
        try {
            $static->allowField(['title','name','status'])->save($data);
            $static = $static->refresh();

            // 更新用户组权限
            $static->updateAllowMenuAndRule($data['menus'],$data['rules'],$data['half_menus']);

            $static->commit();

        } catch (\Exception $e) {
            $static->rollback();
            return false;
        }

        return true;
    }

    /**
     * 编辑权限角色信息
     * @param array $data
     * @return bool
     */
    public static function editAuthRule(array $data)
    {
        $validate = new Validate();
        if (!$validate->scene('edit')->check($data)) {
            throw new ParameterException([
                'msg' => $validate->getError()
            ]);
        }

        // 更新数据库中的数据
        $static = static::find($data['id']);
        $static->startTrans();
        try {
            $static->allowField(['id','title','name','status'])->save($data);

            $static = $static->refresh();

            // 更新用户组权限
            $static->updateAllowMenuAndRule($data['menus'],$data['rules'],$data['half_menus']);

            $static->commit();

        } catch (\Exception $e) {
            $static->rollback();
            return false;
        }

        return true;
    }

    /**
     * 删除权限角色信息
     * @param $ids
     * @return bool
     */
    public static function delAuthRole($ids)
    {
        $validate = new IDCollection();
        if (!$validate->check(['ids' => $ids])) {
            throw new ParameterException([
                'msg' => $validate->getError()
            ]);
        }

        if (!empty($ids)) {
            $admin_data = AdminModel::where('role_id','in', $ids)->count('id');
            if (!empty($admin_data)) {
                throw new HaveUseException([
                    'msg' => '还有管理员正在使用该角色，请确保无管理员使用后再删除'
                ]);
            }
        }

        if (static::destroy($ids) !== false) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 更新当前角色的菜单和规则信息
     * @param $newMenuIDs
     * @param $newRuleIDs
     * @param $halfMenuIDs
     */
    private function updateAllowMenuAndRule($newMenuIDs, $newRuleIDs, $halfMenuIDs = [])
    {
        // 判断传入的数据是否为数组
        if (!is_array($newMenuIDs)) {
            $newMenuIDs = [];
        }
        if (!is_array($newRuleIDs)) {
            $newRuleIDs = [];
        }
        if (!is_array($halfMenuIDs)) {
            $halfMenuIDs = [];
        }

        $oldMenuIDs = $this->allowMenuIDs();
        $oldHalfMenuIDs = $this->allowHalfMenuIDs();
        $oldRuleIDs = $this->allowRuleIDs();

        // 删除中间表的旧数据
        if (!empty($oldMenuIDs)) {
            $this->menus()->detach($oldMenuIDs);
        }
        if (!empty($oldHalfMenuIDs)) {
            $this->half_menus()->detach($oldHalfMenuIDs);
        }
        if (!empty($oldRuleIDs)) {
            $this->rules()->detach($oldRuleIDs);
        }

        if(!empty($newMenuIDs) && !empty($halfMenuIDs)) {
            $newMenuIDs = array_merge($newMenuIDs, $halfMenuIDs);
        }

        // 往中间表添加新数据
        if (!empty($newMenuIDs)) {
            $this->menus()->saveAll($newMenuIDs);
        }
        if (!empty($halfMenuIDs)) {
            $this->half_menus()->saveAll($halfMenuIDs);
        }
        if (!empty($newRuleIDs)) {
            $this->rules()->saveAll($newRuleIDs);
        }
    }

    /**
     * 获取当前角色的所拥有的菜单id
     * @return array
     */
    private function allowMenuIDs()
    {
        return AuthRoleMenuModel::where([['role_id','=', $this->id]])->column('menu_id');
    }

    /**
     * 获取当前角色的所拥有的半选节点菜单id
     * @return array
     */
    private function allowHalfMenuIDs()
    {
        return AuthRoleHalfMenuModel::where([['role_id','=', $this->id]])->column('menu_id');
    }

    /**
     * 获取当前角色的所拥有的权限规则id
     * @return array
     */
    private function allowRuleIDs()
    {
        return AuthRoleRuleModel::where('role_id','=', $this->id)->column('rule_id');
    }

    /**
     * 获取需要删除的id的数据
     * @param $newIDs
     * @param $oldIDs
     * @return array
     */
    private function getDeleteIDs($newIDs, $oldIDs)
    {
        if (empty($oldIDs)) {
            return [];
        } else if (empty($newIDs)) {
            return $oldIDs;
        } else {
            return array_diff($oldIDs, $newIDs);
        }
    }
}